﻿using System.Collections;
using UnityEngine;
using System;
using System.Threading;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using IMIForUnity;
using SimplePopup;

namespace IMIForUnity
{

    public class ImiManagerImp
    {

        private const int TIME_OUT = 20;
        private const int USER_NUM = 10;
        private const int THREAD_INTERVAL = 20;
        private const int TOTAL_JOINTS = (int)ImiSkeleton.Index.COUNT;

        private ImiManager imiManager;
        private static ImiManagerImp instance;
        private MainThreadDispatcher mainThreadDispatcher;
        private ImiAndroidHelper androidHelper;

        private ImiPreferences preferences;
        private ImiManager.OnControlPlayerChanged controlPlayerChangerObserver = null;
        private ImiManager.OnPlayerEntered playerEnteredObserver = null;
        private ImiManager.OnPlayerLost playerLostObserver = null;
        private ImiManager.DepthDataProcessor depthDataProcessor = null;
        private ImiManager.ColorDataProcessor colorDataProcessor = null;
        private ImiManager.RawDataProcessor rawDataProcessor = null;

   //     private TimeSpan startTimeSpan = new TimeSpan(DateTime.Now.Ticks);

        internal enum DeviceOpenState
        {
            OPENING_DEVICE,
            DEVICE_OPENED,
            OPENING_STREAM,
            WORKING,
            CLOSING_STREAM,
            CLOSING_DEVICE,
            DEVICE_CLOSED,
        }
        //If somatosensory device is inited
        internal DeviceOpenState deviceOpenState = DeviceOpenState.DEVICE_CLOSED;


        /// <summary>
        /// 所有玩家体感数据存储数组.
        /// </summary>
        public Dictionary<int, ImiPlayerInfo> playerInfos;

        private Dictionary<ImiTexture2D.Type, ImiTexture2D> imiTextures;

#if UNITY_ANDROID && !UNITY_EDITOR
        private Texture2D allUserColorTexImproved;
#endif

        public ImiWrapper.ImiSkeletonFrame skeletonFrame;

        /// <summary>
        /// 是否需要超过两人的骨架处理.
        /// </summary>
        public bool isNeedMorePlayer = false;

        /// <summary>
        /// 主控玩家ID.
        /// </summary>
        private int controlPlayerId = -1;
        /// <summary>
        /// 副控玩家ID.
        /// </summary>
        private int subControlPlayerId = -2;

        private List<Vector3>[,] distanceDatas;
        private List<Vector4>[,] speedDatas;
        private List<DateTime>[,] timeDatas;

        public int speedFrameLimit = 5;

        private ErrorCode retDepth = ErrorCode.NONE;
        private ErrorCode retColor = ErrorCode.NONE;

        private bool isPrcStop = true;

        //主控玩家选择模式
        private PlayerControlMode controlMode = PlayerControlMode.NEAR;

        //Used for getting real time
        //private DateTime epochStart;

        private ushort[] depthDataRaw;
        private byte[] colorDataRaw;

        private bool prcSkeletonLock = false;

        private static object _lock = new object();

        private ImiGestureManager imiGestureManager = ImiGestureManager.GetInstance();

        public static ImiManagerImp GetInstance()
        {
            if (instance == null)
            {
                lock (_lock)
                {
                    if (instance == null)
                    {
                        instance = new ImiManagerImp();
                    }
                }
            }
            return instance;
        }

        private ImiManagerImp()
        {
            imiManager = ImiManager.GetInstance();
            mainThreadDispatcher = MainThreadDispatcher.GetInstance();
            androidHelper = ImiAndroidHelper.GetInstance();

#if UNITY_ANDROID && !UNITY_EDITOR
            mainThreadDispatcher.Enqueue(readStreamingAssets());
#endif
        }

        IEnumerator readStreamingAssets()
        {
            string filePath = "";
            string toPath;
            string dirPath = "/data/data/" + Application.bundleIdentifier + "/files/";
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            foreach (string fileName in filesNamesToCopy)
            {
                filePath = System.IO.Path.Combine(Application.streamingAssetsPath, fileName);
                toPath = dirPath + fileName;
                if (File.Exists(toPath))
                {
                    continue;
                }
                WWW www = new WWW(filePath);
                yield return www;
                File.WriteAllBytes(toPath, www.bytes);
            }
        }

        internal void Update()
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            if (openLater)
            {
                Open();
            }
#endif
            if (deviceOpenState == DeviceOpenState.WORKING)
            {
                //Update Color Textures
                //if (colorQueue != null && colorQueue.Count > 0 && imiTextures.ContainsKey(ImiTexture2D.Type.COLOR))
                //{
                //    //重要结论：在下面循环中更新texture,然后再设置dataready，会显著降低帧率
                //    //建议把彩色图拎出来
                //    dataHolder.setColorDataRaw(colorQueue.Dequeue());
                //    imiTextures[ImiTexture2D.Type.COLOR].UpdateTexture();
                //}

                //if (retDepth == ErrorCode.OK)
                //{
                //    depthTexture.LoadRawTextureData(depthTexData0);
                //    depthTexture.Apply();
                //}
                List<ImiTexture2D.Type> keys = new List<ImiTexture2D.Type>(imiTextures.Keys);
                for (int i = 0; i < keys.Count; i++)
                {
                    imiTextures[keys[i]].UpdateTexture();
                }

                ////telling the holder to prepare next frame
                //dataHolder.SetTextureUsed();

#if UNITY_ANDROID && !UNITY_EDITOR
            if (deviceOpenState == DeviceOpenState.WORKING && imiManager.IsPerformance)
            {
                if (preferences.ColorStream)
                {
                    IssuePluginEvent(MediaSurfaceEventType.Update);
                }
            }
#endif

            }
        }
        private string[] filesNamesToCopy = new string[]
        {
            "FeatureExtraction.ini",
            "GlobalDefaults.ini",
            "h.dat",
            "imi.ini",
            "imi.ini.release",
            "licenses.xml",
            "modules.xml",
            "Nite.ini",
            "s.dat",
            "SamplesConfig.xml",
            "UserCalibration.bin",
            "Matixdata.txt"
        };

        private bool copiedAllConfig()
        {
            string filePath = "";
            string toPath;
            string dirPath = "/data/data/" + Application.bundleIdentifier + "/files/";
            if (!Directory.Exists(dirPath))
            {
                return false;
            }
            foreach (string fileName in filesNamesToCopy)
            {
                filePath = System.IO.Path.Combine(Application.streamingAssetsPath, fileName);
                toPath = dirPath + fileName;
                if (!File.Exists(toPath))
                {
                    return false;
                }
            }
            return true;
        }

        private bool openLater = false;
        /// <summary>
        /// Open Device And Open Streams
        /// </summary>
        public void Open()
        {

#if UNITY_ANDROID && !UNITY_EDITOR
            //If Not All Configuration Files is copied, wait
            if (!copiedAllConfig())
            {
                openLater = true;
                return;
            }
            openLater = false;
            //while (!copiedAllConfig());
#endif 

#if UNITY_ANDROID && !UNITY_EDITOR
            if (imiManager.useRootPermission)
            {
                realInit();
            }
            else
            {
                if(deviceOpenState == DeviceOpenState.DEVICE_CLOSED)
                {
                    Log.d("Android Platform");
                    UseAndroidPermission();
                }else
                {
                    Log.d("Device Is Opening, Don't call again");
                }
            }
#else
            realInit();
#endif
        }

        private bool isGetttingPermission = false;
        public void UseAndroidPermission()
        {
            if (isGetttingPermission)
            {
                return;
            }
            isGetttingPermission = true;
            androidHelper = ImiAndroidHelper.GetInstance();
            androidHelper.RequirePermission(delegate (ErrorCode code)
            {
                isGetttingPermission = false;
                if (code == ErrorCode.OK)
                {
                    Log.d("Permission Granted, Trying to initialize device");
                    realInit();
                }
                else
                {
                    Log.e("Device Inited failed! Maybe permission is denied!");
                    Log.e("We can try again!");
                    realInit();
                }
            });
        }

        static void UnityLog(string log)
        {
            Log.d("Native: " + log);
        }

        static void OnUpgradeDownloadedImpl()
        {
            Log.d("Update.zip Downloaded! Start Upgrading...");
            MainThreadDispatcher.GetInstance().Enqueue(ImiManagerImp.ShowPopup);
        }

        private static void OnDeviceUpgradingImpl(ImiWrapper.IMI_UPGRADE_PROMPT promptId, float progress)
        {
            MainThreadDispatcher.GetInstance().Enqueue(() => { showUpgradeProgress(promptId, progress); });
        }

        private static void showUpgradeProgress(ImiWrapper.IMI_UPGRADE_PROMPT promptId, float progress)
        {
            if (promptId == ImiWrapper.IMI_UPGRADE_PROMPT.IMI_UPLOADBLOCK_UPLOADING
                || promptId == ImiWrapper.IMI_UPGRADE_PROMPT.IMI_REBOOTING)
            {
                if (SimplePopupManager.Instance != null)
                {
                    SimplePopupManager.Instance.SetProgress(progress * 100);
                }
            }
            else if (promptId == ImiWrapper.IMI_UPGRADE_PROMPT.IMI_UPGRADE_PASS)
            {
                OnUpdateFinished();
            }
            else if (promptId == ImiWrapper.IMI_UPGRADE_PROMPT.IMI_UPGRADE_FAIL)
            {
                OnUpdateFinished();
            }
            //IMI_REBOOTING
            Log.d("Device Upgrading...PromptId = " + promptId + " progress = " + progress);
        }

        private void InitUpgradeCallback()
        {

            ImiWrapper.OnUpgradeDownloaded downloadedCallback = new ImiWrapper.OnUpgradeDownloaded(OnUpgradeDownloadedImpl);
            System.IntPtr downloadedPtr = Marshal.GetFunctionPointerForDelegate(downloadedCallback);

            ImiWrapper.OnDeviceUpgrading upgradingCallback = new ImiWrapper.OnDeviceUpgrading(OnDeviceUpgradingImpl);
            System.IntPtr upgradingPtr = Marshal.GetFunctionPointerForDelegate(upgradingCallback);
            ImiWrapper.SetDeviceUpgradeCallback(downloadedPtr, upgradingPtr);
            Log.d("Calling Upgrade!");

        }

        private void InitLogCallback()
        {
            ImiWrapper.UnityLog logCallback = new ImiWrapper.UnityLog(UnityLog);
            System.IntPtr logPtr = Marshal.GetFunctionPointerForDelegate(logCallback);
            ImiWrapper.SetLogFunction(logPtr);
        }

        public static void OnUpdateFinished()
        {
            SimplePopupManager.Instance.CreatePopup("升级完成，请重新打开游戏！");
            SimplePopupManager.Instance.AddButton("退出", delegate { ImiManager.GetInstance().CloseDevice(); Application.Quit(); });
            SimplePopupManager.Instance.ShowPopup();
        }

        public static void OnUpdateFailed()
        {
            SimplePopupManager.Instance.CreatePopup("失败，请退出重试！");
            SimplePopupManager.Instance.AddButton("退出", delegate { ImiManager.GetInstance().CloseDevice(); Application.Quit(); });
            SimplePopupManager.Instance.ShowPopup();
        }


        public static void ShowPopup()
        {
            SimplePopupManager.Instance.CreatePopup("系统检测到固件更新，是否升级？");
            SimplePopupManager.Instance.AddButton("Yes", delegate { ImiWrapper.SetUpgrade(true); ShowProgress(); });
            SimplePopupManager.Instance.AddButton("No", delegate { Log.d("clicked on no"); });
            SimplePopupManager.Instance.ShowPopup();
        }

        private bool showed = false;
        public static void ShowProgress()
        {
            SimplePopupManager.Instance.CreatePopup("正在升级中，请不要拔掉电源...");
            SimplePopupManager.Instance.EnableProgressBar(true);
            SimplePopupManager.Instance.SetProgress(0);
            SimplePopupManager.Instance.ShowPopup();
        }

#if (UNITY_ANDROID && !UNITY_EDITOR)
        private enum MediaSurfaceEventType
        {
            Initialize = 0,
            Shutdown = 1,
            Update = 2,
            Max_EventType
        };

        public static int eventBase
        {
            get { return _eventBase; }
            set
            {
                _eventBase = value;
                if (ImiManager.GetInstance() != null)
                {
                    if (ImiManager.GetInstance().IsPerformance)
                    {
			            ImiWrapper.OVR_Media_Surface_SetEventBase(_eventBase);
                    }
                }
            }
        }
        private static int _eventBase = 0;

        private static void IssuePluginEvent(MediaSurfaceEventType eventType)
        {
#pragma warning disable CS0618 // Type or member is obsolete
            GL.IssuePluginEvent((int)eventType + eventBase);
#pragma warning restore CS0618 // Type or member is obsolete
        }


#endif
        private void realInit()
        {
            //If already inited, return
            if (deviceOpenState != DeviceOpenState.DEVICE_CLOSED)
            {
                Log.d("Device is not Closed now, it might be already opened, or you are opening/closing it");
                return;
            }

            InitPreferences();

#if UNITY_ANDROID && !UNITY_EDITOR
            if (imiManager.IsPerformance)
            {
                if (preferences.ColorStream)
                {
                    IssuePluginEvent(MediaSurfaceEventType.Initialize);

                    allUserColorTexImproved = Texture2D.CreateExternalTexture(preferences.colorResolution.Width, preferences.colorResolution.Height, TextureFormat.RGB24, false, false, System.IntPtr.Zero);
                    allUserColorTexImproved.filterMode = FilterMode.Point;
                    allUserColorTexImproved.Apply();

                    ImiWrapper.Imi_Renderer_Surface(allUserColorTexImproved.GetNativeTexturePtr(), preferences.colorResolution.Width, preferences.colorResolution.Height);
                }
            }
#endif

            //If upgrade the firmware and drivers(not unity sdk)
            if (imiManager.AutoUpgrade)
            {
                InitUpgradeCallback();
            }

            InitLogCallback();

            //Step One : Open Device and streams in sub thread
            //Step Two will be called afterwards in sub thread
            Thread openDeviceThread = new Thread(new ThreadStart(OpenStepOne));
            openDeviceThread.Start();

        }

        //This method should be used in sub thread to avoid blocking the main thread
        private void OpenStepOne()
        {
            if (IminectInitialize())
            {
                mainThreadDispatcher.Enqueue(OpenStepTwo);
            }
            else
            {
                Log.e("Open Device Failed! Closing...");
                mainThreadDispatcher.Enqueue(Close);
                imiManager._OnDeviceOpened();
            }
        }

        //When the device is opened, init other resources
        private void OpenStepTwo()
        {
            InitializeDisplayImageData();
            InitializeIminectData();

            InitPrcImiDataThd();

            Log.d("Device is Opened!");
            imiManager._OnDeviceOpened();
        }

        public void Close()
        {
            if (deviceOpenState == DeviceOpenState.DEVICE_CLOSED)
            {
                Log.e("Device Has Already Closed! Don't Close Again!");
                imiManager._OnDeviceClosed();
            }
            if (deviceOpenState != DeviceOpenState.WORKING &&
                deviceOpenState != DeviceOpenState.DEVICE_OPENED)
            {
                Log.e("Device is not in the right state to close or has already been closed!");
                return;
            }

            ErrorCode error = ErrorCode.OK;

            if (deviceOpenState == DeviceOpenState.WORKING)
            {
                error = CloseStream();
            }

            if (error != ErrorCode.OK)
            {
                Log.e("Closing Streams Failed");
                return;
            }

            if (deviceOpenState == DeviceOpenState.DEVICE_OPENED)
            {
                error = CloseDevice();
            }

            if (error != ErrorCode.OK)
            {
                Log.e("Closing Device Failed");
                return;
            }

            isPrcStop = true;

            //Restore all the resources
#if UNITY_ANDROID && !UNITY_EDITOR
            if (androidHelper != null)
                androidHelper.shutdown();
#endif

            ReleaseIminectData();
            ReleaseDisplayImageData();

            //Tell GC to collect memory
            GC.Collect();
            GC.WaitForPendingFinalizers();

#if UNITY_ANDROID && !UNITY_EDITOR
            if (preferences.ColorStream)
            {
                IssuePluginEvent(MediaSurfaceEventType.Shutdown);
            }
#endif

            imiManager._OnDeviceClosed();
            Log.d("Device Is Closed");

        }

        public ErrorCode CloseStream()
        {
            if (deviceOpenState != DeviceOpenState.WORKING)
            {
                Log.e("Streams has not been opened or device is closed!");
                return ErrorCode.ERROR;
            }
            ErrorCode error = ErrorCode.OK;
            deviceOpenState = DeviceOpenState.CLOSING_STREAM;

            if (preferences.ColorStream)
            {
                error = ImiWrapper.CloseStream(ImiWrapper.ImiFrameType.IMI_COLOR_FRAME);

                if (error != ErrorCode.OK)
                {
                    Log.e("Close Color Stream Failed");
                    deviceOpenState = DeviceOpenState.WORKING;
                    return ErrorCode.CLOSE_COLOR_STREAM_FAILED;
                }
            }

            if (preferences.DepthStream)
            {
                error = ImiWrapper.CloseStream(ImiWrapper.ImiFrameType.IMI_DEPTH_FRAME);
            }

            if (preferences.SkeletonStream)
            {
                error = ImiWrapper.CloseStream(ImiWrapper.ImiFrameType.IMI_SKELETON_FRAME);
            }

            if (preferences.DepthSkeletonStream)
            {
                error = ImiWrapper.CloseStream(ImiWrapper.ImiFrameType.IMI_DEPTH_SKELETON_FRAME);
            }

            if (preferences.UserDepthAndSkeletonStream)
            {
                error = ImiWrapper.CloseStream(ImiWrapper.ImiFrameType.IMI_USER_INDEX_SKELETON_FRAME);
            }

            if (error != ErrorCode.OK)
            {
                Log.e("Close Stream Failed, error = " + error);
                deviceOpenState = DeviceOpenState.WORKING;
            }
            else
            {
                deviceOpenState = DeviceOpenState.DEVICE_OPENED;
            }

            return error;
        }

        private ErrorCode CloseDevice()
        {
            if (deviceOpenState != DeviceOpenState.DEVICE_OPENED)
            {
                Log.e("Device is not opened or streams has not been closed");
                return ErrorCode.ERROR;
            }

            ErrorCode error = ErrorCode.OK;
            deviceOpenState = DeviceOpenState.CLOSING_DEVICE;

            error = ImiWrapper.CloseDevice();
            if (error != ErrorCode.OK)
            {
                deviceOpenState = DeviceOpenState.DEVICE_OPENED;
                Log.e(error);
            }
            else
            {
                deviceOpenState = DeviceOpenState.DEVICE_CLOSED;
            }
            return error;
        }

        private void InitPrcImiDataThd()
        {
            if (deviceOpenState != DeviceOpenState.WORKING)
            {
                Log.e("Device is in opening state, so don't open process threads");
                return;
            }
            if (!isPrcStop)
            {
                Log.e("These Threads might have already been opened!");
            }

            isPrcStop = false;
            if (imiManager.UserColorTexture)
            {
                Thread prcCDThd = new Thread(new ThreadStart(prcColorAndDepthData));
                prcCDThd.Start();
            }
            else
            {
                if (preferences.DepthSkeletonStream || preferences.SkeletonStream || preferences.DepthStream || preferences.UserDepthAndSkeletonStream)
                {
                    Thread prcDSThd = new Thread(new ThreadStart(PrcDepthData));
                    prcDSThd.Start();
                }

                if (preferences.ColorStream)
                {
                    Thread prcColorThd = new Thread(new ThreadStart(PrcColorData));
                    prcColorThd.Start();
                }
            }

            Log.d("Thread Started!");
        }

        /// <summary>
        /// Init user defined properties
        /// </summary>
        private void InitPreferences()
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            if(androidHelper.GetAndroidAPILevel() < 21)
            {
                Log.e("Performance mode is not supported under android 5.0, disable performance");
                imiManager.IsPerformance = false;
            }
#endif

            preferences = ImiPreferences.GetInstance();
            preferences.ColorStream = false;
            preferences.DepthSkeletonStream = false;
            preferences.UserDepthAndSkeletonStream = false;
            preferences.DepthStream = false;
            preferences.SkeletonStream = false;

            if (imiManager.UserColorTexture)
            {
                imiManager.imageResolution = ImiResolution.Type._640x480;
                imiManager.IsPerformance = false;
                preferences.ColorStream = true;
                preferences.UserDepthAndSkeletonStream = true;
            }
            else
            {
                if (imiManager.ColorTexture)
                {
                    preferences.ColorStream = true;
                }

                if (imiManager.UserDepthTexture)
                {
                    preferences.UserDepthAndSkeletonStream = true;
                }
                else if (imiManager.DepthTexture && imiManager.SkeletonData)
                {
                    preferences.DepthSkeletonStream = true;
                }
                else if (imiManager.DepthTexture)
                {
                    preferences.DepthStream = true;
                }
                else if (imiManager.SkeletonData)
                {
                    preferences.SkeletonStream = true;
                }
            }

            if (!imiManager.IsPerformance && imiManager.imageResolution == ImiResolution.Type._1920x1080)
            {
                Log.e("1920x1080 resolution cannot be used without performance enabled");
                imiManager.imageResolution = ImiResolution.Type._1280x720;
            }

            if(imiManager.imageResolution == ImiResolution.Type._320x240)
            {
                Log.e("320x240 resolution is not supported for color image! Change to 640x480");
                imiManager.imageResolution = ImiResolution.Type._640x480;
            }

            if (imiManager.depthResolution != ImiResolution.Type._640x480 
                && imiManager.depthResolution != ImiResolution.Type._320x240)
            {
                Log.e("DepthResolution only supports 640x480 and 320x240 now");
                imiManager.depthResolution = ImiResolution.Type._320x240;
            }

            preferences.colorResolution.type = imiManager.imageResolution;
            preferences.depthResolution.type = imiManager.depthResolution;
        }


        //绘制相关数据初始化.
        private void InitializeDisplayImageData()
        {
            imiTextures = new Dictionary<ImiTexture2D.Type, ImiTexture2D>();
        }

        private void ReleaseDisplayImageData()
        {
            if (imiTextures != null)
            {
                List<ImiTexture2D.Type> keys = new List<ImiTexture2D.Type>(imiTextures.Keys);
                foreach (ImiTexture2D.Type key in keys)
                {
                    imiTextures[key].ReleaseTexture();
                }
                imiTextures.Clear();
            }
            imiTextures = null;
            depthDataRaw = null;
            colorDataRaw = null;
            depthDataProcessor = null;
            colorDataProcessor = null;

        }


        /// <summary>
        /// Initialize somatosensor data
        /// </summary>
        private void InitializeIminectData()
        {
            playerInfos = new Dictionary<int, ImiPlayerInfo>();

            distanceDatas = new List<Vector3>[USER_NUM, TOTAL_JOINTS];
            speedDatas = new List<Vector4>[USER_NUM, TOTAL_JOINTS];
            timeDatas = new List<DateTime>[USER_NUM, TOTAL_JOINTS];
            for (int i = 0; i < USER_NUM; i++)
            {
                for (int j = 0; j < TOTAL_JOINTS; j++)
                {
                    distanceDatas[i, j] = new List<Vector3>();
                    speedDatas[i, j] = new List<Vector4>();
                    timeDatas[i, j] = new List<DateTime>();
                }
            }
            //epochStart = new System.DateTime(1970, 1, 1, 8, 0, 0, System.DateTimeKind.Utc);
        }

        private void ReleaseIminectData()
        {
            if (playerInfos != null)
            {
                playerInfos.Clear();
                for (int i = 0; i < USER_NUM; i++)
                {
                    for (int j = 0; j < TOTAL_JOINTS; j++)
                    {
                        distanceDatas[i, j].Clear();
                        distanceDatas[i, j] = null;
                        speedDatas[i, j].Clear();
                        speedDatas[i, j] = null;
                        timeDatas[i, j].Clear();
                        timeDatas[i, j] = null;
                    }
                }
                distanceDatas = null;
                speedDatas = null;
                timeDatas = null;
                playerInfos = null;
            }
        }

        /// <summary>
        /// Open Device and Open Streams according to the user choice
        /// </summary>
        private bool IminectInitialize()
        {
            //Returns if Device is unavailable
            //Returns if Already Initialized
            if (!IsDeviceAvailable())
            {
                Log.e("Device is Unavailable!");
                return false;
            }
            if (deviceOpenState != DeviceOpenState.DEVICE_CLOSED)
            {
                Log.e("Device is not on the right state to be opened! Please close it first, or use OpenStream instead");
                return false;
            }

            if ((OpenDevice()) != ErrorCode.OK)
            {
                return false;
            }

            if (RealOpenStream() != ErrorCode.OK)
            {
                return false;
            }

            return true;
        }

        public ErrorCode OpenDevice()
        {
            if (deviceOpenState != DeviceOpenState.DEVICE_CLOSED)
            {
                Log.e("Device State is not Closed");
                return ErrorCode.ERROR;
            }

            deviceOpenState = DeviceOpenState.OPENING_DEVICE;

            ErrorCode error = ErrorCode.OK;
#if UNITY_ANDROID && !UNITY_EDITOR
            if (imiManager.useRootPermission)
            {
                //open device on rooted system
                if ((ImiWrapper.OpenDevice()) != ErrorCode.OK)
                {
                    Log.e(error);
                    error = ErrorCode.OPEN_DEVICE_FAILED;
                }
            }
            else
            {
                //return if the device has not been opened
                if (androidHelper.fd == -1)
                {
                    error = ErrorCode.OPEN_DEVICE_FAILED;
                }

                if (androidHelper.fd == 0)
                {
                    //open device on rooted system
                    if ((error = ImiWrapper.OpenDevice()) != ErrorCode.OK)
                    {
                        Log.e(error);
                        error = ErrorCode.OPEN_DEVICE_FAILED;
                    }
                }
                else
                {
                    if ((ImiWrapper.OpenDevice2(androidHelper.fd, androidHelper.usbPath)) != ErrorCode.OK)
                    {
                        Log.e(error);
                        error = ErrorCode.OPEN_DEVICE_FAILED;
                    }
                }
            }

#else
            //open device on rooted system
            if (ImiWrapper.OpenDevice() != ErrorCode.OK)
            {
                error = ErrorCode.OPEN_DEVICE_FAILED;
            }
#endif
            if (error != ErrorCode.OK)
            {
                deviceOpenState = DeviceOpenState.DEVICE_CLOSED;
            }
            else
            {
                deviceOpenState = DeviceOpenState.DEVICE_OPENED;
            }

            return error;
        }

        public void OpenStream()
        {
            Thread openStreamThd = new Thread(new ThreadStart(OpenStreamThread));
            openStreamThd.Start();
        }

        private void OpenStreamThread()
        {
            RealOpenStream();
        }

        private ErrorCode RealOpenStream()
        {
            if (deviceOpenState != DeviceOpenState.DEVICE_OPENED)
            {
                Log.e("Device State is not on opened state. You need to open device first or close current streams");
                return ErrorCode.ERROR;
            }
            deviceOpenState = DeviceOpenState.OPENING_STREAM;

            ErrorCode error = ErrorCode.NONE;
            //Open Depth/Skeleton Stream
            ImiWrapper.ImiFrameType streamType = ImiWrapper.ImiFrameType.IMI_USER_INDEX_SKELETON_FRAME;

            if (preferences.UserDepthAndSkeletonStream)
            {
                streamType = ImiWrapper.ImiFrameType.IMI_USER_INDEX_SKELETON_FRAME;
            }
            else if (preferences.DepthSkeletonStream)
            {
                streamType = ImiWrapper.ImiFrameType.IMI_DEPTH_SKELETON_FRAME;
            }
            else if (preferences.DepthStream)
            {
                streamType = ImiWrapper.ImiFrameType.IMI_DEPTH_FRAME;
            }
            else if (preferences.SkeletonStream)
            {
                streamType = ImiWrapper.ImiFrameType.IMI_SKELETON_FRAME;
            }
            else
            {
                //Don't open any kind of depth stream or skeleton stream
                error = ErrorCode.OK;
            }
            if (error == ErrorCode.NONE)
            {
                error = ImiWrapper.OpenStream(streamType,
                    preferences.depthResolution.type,
                    ImiWrapper.ImiPixelFormat.IMI_PIXEL_FORMAT_DEP_16BIT);

                if (error == ErrorCode.OK)
                {
                    Log.d("Open  " + streamType + " OK");
                }
                else
                {
                    Log.e("Open " + streamType + " Failed, error = " + error);
                    deviceOpenState = DeviceOpenState.DEVICE_OPENED;
                    return error;
                }
            }

            //Open Color Stream
            if (preferences.ColorStream)
            {

#if UNITY_ANDROID && !UNITY_EDITOR
                if (imiManager.IsPerformance)
                {
                    if ((error = ImiWrapper.OpenStream(ImiWrapper.ImiFrameType.IMI_COLOR_FRAME,
                        preferences.colorResolution.type,
                        ImiWrapper.ImiPixelFormat.IMI_PIXEL_FORMAT_IMAGE_H264))
                    != ErrorCode.OK)
                    {
                        Log.e(error);
                    }
                }else
                {
                    if ((error = ImiWrapper.OpenStream(ImiWrapper.ImiFrameType.IMI_COLOR_FRAME,
                        preferences.colorResolution.type,
                        ImiWrapper.ImiPixelFormat.IMI_PIXEL_FORMAT_IMAGE_RGB24))
                    != ErrorCode.OK)
                    {
                        Log.e(error);
                    }
                }
#else
                if ((error = ImiWrapper.OpenStream(ImiWrapper.ImiFrameType.IMI_COLOR_FRAME,
                    preferences.colorResolution.type,
                    ImiWrapper.ImiPixelFormat.IMI_PIXEL_FORMAT_IMAGE_RGB24))
                != ErrorCode.OK)
                {
                    Log.e(error);
                }
#endif
                if (error == ErrorCode.OK)
                {
                    Log.d("Open Color Stream Ok!");
                }
                else
                {
                    Log.e("Open Color Stream Failed");
                }
            }
            if (error != ErrorCode.OK)
            {
                deviceOpenState = DeviceOpenState.DEVICE_OPENED;
            }
            else
            {
                deviceOpenState = DeviceOpenState.WORKING;
            }
            return error;

        }


        public bool IsDeviceAvailable()
        {
#if UNITY_ANDROID && !UNITY_EDITOR
            if (imiManager.useRootPermission)
            {
                return true;
            }else
            {
                return androidHelper.fd != -1;
            }
#else
            return true;
#endif
        }


        private bool showDepthFrame()
        {
            return preferences.SkeletonStream || preferences.DepthStream || preferences.UserDepthAndSkeletonStream || preferences.DepthSkeletonStream;
        }

        private void PrcColorData()
        {
            while (!isPrcStop)
            {
                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();

                if (deviceOpenState == DeviceOpenState.WORKING)
                {
#if UNITY_ANDROID && !UNITY_EDITOR
                    if (imiManager.IsPerformance)
                    {
                        ImiWrapper.LoadColorTexture(ImiWrapper.ImiPixelFormat.IMI_PIXEL_FORMAT_IMAGE_H264);//H264
                        //Thread.Sleep(THREAD_INTERVAL);
                    }else{
#endif
                    if (colorDataProcessor != null)
                    {
                        retColor = ImiWrapper.GetColorData(colorDataRaw, TIME_OUT);
                        if (retColor == ErrorCode.OK)
                        {
                            colorDataProcessor(colorDataRaw);
                        }
                    }
                    else if (imiTextures.ContainsKey(ImiTexture2D.Type.COLOR))
                    {
                        if (imiTextures[ImiTexture2D.Type.COLOR].texStatus == ImiTexture2D.Status.USED)
                        {
                            imiTextures[ImiTexture2D.Type.COLOR].texStatus = ImiTexture2D.Status.READING;
                            retColor = ImiWrapper.GetColorData(imiTextures[ImiTexture2D.Type.COLOR].textureData, TIME_OUT);
                            if (retColor == ErrorCode.OK)
                            {
                                imiTextures[ImiTexture2D.Type.COLOR].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                            }
                            else
                            {
                                imiTextures[ImiTexture2D.Type.COLOR].texStatus = ImiTexture2D.Status.USED;
                            }
                        }
                    }
                    else
                    {
                        //Log.e("No Way to process Color Data");
                    }


#if UNITY_ANDROID && !UNITY_EDITOR
                    }
#endif

                    if (watch.ElapsedMilliseconds < THREAD_INTERVAL - 5)
                    {
                        Thread.Sleep(THREAD_INTERVAL - (int)watch.ElapsedMilliseconds);
                    }

                }
            }
        }

        //体感数据处理.
        private void PrcDepthData()
        {
            while (!isPrcStop)
            {

                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                if (deviceOpenState == DeviceOpenState.WORKING)
                {
                    retDepth = ErrorCode.NONE;
                    if (depthDataProcessor != null)
                    {
                        if (preferences.DepthSkeletonStream)
                        {
                            retDepth = ImiWrapper.GetDepthDataAndSkeletonFrame(depthDataRaw, ref skeletonFrame, TIME_OUT);
                        }
                        else if (preferences.DepthStream)
                        {
                            retDepth = ImiWrapper.GetDepthData(depthDataRaw, TIME_OUT);
                        }
                        else if (preferences.UserDepthAndSkeletonStream)
                        {
                            retDepth = ImiWrapper.GetDepthPlayerDataAndSkeletonFrame(depthDataRaw, ref skeletonFrame, TIME_OUT);
                        }
                        if (retDepth == ErrorCode.OK)
                        {
                            depthDataProcessor(depthDataRaw);
                        }
                    }
                    else
                    {

                        if (preferences.DepthSkeletonStream)
                        {
                            if (imiTextures.ContainsKey(ImiTexture2D.Type.DEPTH))
                            {
                                if (imiTextures[ImiTexture2D.Type.DEPTH].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetDepthTexDataAndSkeletonFrame(imiTextures[ImiTexture2D.Type.DEPTH].textureData, null, ref skeletonFrame, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                            else
                            {
                                //Log.d("No Depth Data Processors");
                            }
                        }
                        else if (preferences.DepthStream)
                        {
                            if (imiTextures.ContainsKey(ImiTexture2D.Type.DEPTH))
                            {
                                if (imiTextures[ImiTexture2D.Type.DEPTH].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetDepthTextureData(imiTextures[ImiTexture2D.Type.DEPTH].textureData, null, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.DEPTH].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                            else
                            {
                                //Log.d("No Depth Data Processors");
                            }
                        }
                        else if (preferences.SkeletonStream)
                        {
                            retDepth = ImiWrapper.GetSkeletonFrame(ref skeletonFrame, TIME_OUT);
                        }
                        else if (preferences.UserDepthAndSkeletonStream)
                        {
                            if (imiTextures.ContainsKey(ImiTexture2D.Type.MAIN_USER_DEPTH))
                            {
                                if (imiTextures.ContainsKey(ImiTexture2D.Type.SUB_USER_DEPTH))
                                {
                                    if (imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus == ImiTexture2D.Status.USED &&
                                        imiTextures[ImiTexture2D.Type.SUB_USER_DEPTH].texStatus == ImiTexture2D.Status.USED)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.READING;
                                        imiTextures[ImiTexture2D.Type.SUB_USER_DEPTH].texStatus = ImiTexture2D.Status.READING;
                                        retDepth = ImiWrapper.GetDepthPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].textureData, controlPlayerId, imiTextures[ImiTexture2D.Type.SUB_USER_DEPTH].textureData, subControlPlayerId, null, ref skeletonFrame, TIME_OUT, false);
                                        if (retDepth == ErrorCode.OK)
                                        {
                                            imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                            imiTextures[ImiTexture2D.Type.SUB_USER_DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        }
                                        else
                                        {
                                            imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.USED;
                                            imiTextures[ImiTexture2D.Type.SUB_USER_DEPTH].texStatus = ImiTexture2D.Status.USED;
                                        }
                                    }
                                }
                                else
                                {
                                    if (imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus == ImiTexture2D.Status.USED)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.READING;
                                        retDepth = ImiWrapper.GetDepthPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].textureData, controlPlayerId, null, -1, null, ref skeletonFrame, TIME_OUT, false);
                                        if (retDepth == ErrorCode.OK)
                                        {
                                            imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        }
                                        else
                                        {
                                            imiTextures[ImiTexture2D.Type.MAIN_USER_DEPTH].texStatus = ImiTexture2D.Status.USED;
                                        }
                                    }
                                }
                            }
                            else if (imiTextures.ContainsKey(ImiTexture2D.Type.USER_DEPTH))
                            {
                                if (imiManager.ShowDepthDetail)
                                {
                                    if (imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus == ImiTexture2D.Status.USED)
                                    {
                                        imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.READING;
                                        retDepth = ImiWrapper.GetDepthPlayerTexDataAndSkeletonFrame(imiTextures[ImiTexture2D.Type.USER_DEPTH].textureData, null, ref skeletonFrame, TIME_OUT, false);
                                        if (retDepth == ErrorCode.OK)
                                        {
                                            imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        }
                                        else
                                        {
                                            imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.USED;
                                        }

                                    }
                                }
                                else
                                {
                                    if (imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus == ImiTexture2D.Status.USED)
                                    {
                                        imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.READING;
                                        retDepth = ImiWrapper.GetDepthPlayerTextureData(imiTextures[ImiTexture2D.Type.USER_DEPTH].textureData, 0, null, -1, null, ref skeletonFrame, TIME_OUT, false);
                                        if (retDepth == ErrorCode.OK)
                                        {
                                            imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        }
                                        else
                                        {
                                            imiTextures[ImiTexture2D.Type.USER_DEPTH].texStatus = ImiTexture2D.Status.USED;
                                        }
                                    }


                                }
                            }
                            else
                            {
                                //Log.d("No Depth Data Processors");
                            }

                            //if (watch.ElapsedMilliseconds > 2)
                            //{
                            //    Log.d("reading time: " + watch.ElapsedMilliseconds);
                            //}
                        }
                    }

                    if (retDepth == ErrorCode.OK)
                    {
                        //Debug.Log("Read Depth Data OK");
                        if (preferences.SkeletonStream || preferences.UserDepthAndSkeletonStream || preferences.DepthSkeletonStream)
                        {
                            try
                            {
                                ProcessSkeleton();
                            }
                            catch (Exception e)
                            {
                                Log.d("PrcDepthData Process Skeleton Error :" + e.ToString());
                            }
                        }
                    }
                    else
                    {
                        //Debug.Log("Reading Depth Data Failed, ret =" + retDepth);
                    }
                }

                if (watch.ElapsedMilliseconds < THREAD_INTERVAL - 5)
                {
                    Thread.Sleep(THREAD_INTERVAL - (int)watch.ElapsedMilliseconds);
                }
            }
        }

        private void prcColorAndDepthData()
        {
            while (!isPrcStop)
            {
                System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
                if (deviceOpenState == DeviceOpenState.WORKING)
                {
                    retDepth = ErrorCode.NONE;
                    if (rawDataProcessor != null)
                    {
                        retDepth = ImiWrapper.GetColorAndDepthData(colorDataRaw, depthDataRaw, ref skeletonFrame, TIME_OUT);
                        if (retDepth == ErrorCode.OK)
                        {
                            rawDataProcessor(colorDataRaw, depthDataRaw);
                        }
                    }
                    else
                    {
                        if (imiTextures.ContainsKey(ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED))
                        {
                            if (imiTextures.ContainsKey(ImiTexture2D.Type.SUB_USER_COLOR_CENTERED))
                            {
                                if (imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus == ImiTexture2D.Status.USED &&
                                    imiTextures[ImiTexture2D.Type.SUB_USER_COLOR_CENTERED].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READING;
                                    imiTextures[ImiTexture2D.Type.SUB_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetNormalizedColorPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].textureData, controlPlayerId, imiTextures[ImiTexture2D.Type.SUB_USER_COLOR_CENTERED].textureData, subControlPlayerId, null, null, ref skeletonFrame, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        imiTextures[ImiTexture2D.Type.SUB_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.USED;
                                        imiTextures[ImiTexture2D.Type.SUB_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                            else
                            {
                                if (imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetNormalizedColorPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].textureData, controlPlayerId, null, -1, null, null, ref skeletonFrame, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        

                                        //TimeSpan end = new TimeSpan(DateTime.Now.Ticks);
                                        //TimeSpan dtime = end.Subtract(startTimeSpan).Duration();
                                        //Log.d("[Richard]Unity dtime = " + dtime.TotalMilliseconds.ToString());

                                        //startTimeSpan = new TimeSpan(DateTime.Now.Ticks);
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                        }
                        else if (imiTextures.ContainsKey(ImiTexture2D.Type.MAIN_USER_COLOR))
                        {
                            if (imiTextures.ContainsKey(ImiTexture2D.Type.SUB_USER_COLOR))
                            {
                                if (imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus == ImiTexture2D.Status.USED &&
                                    imiTextures[ImiTexture2D.Type.SUB_USER_COLOR].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.READING;
                                    imiTextures[ImiTexture2D.Type.SUB_USER_COLOR].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetColorPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].textureData, controlPlayerId, imiTextures[ImiTexture2D.Type.SUB_USER_COLOR].textureData, subControlPlayerId, null, null, ref skeletonFrame, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                        imiTextures[ImiTexture2D.Type.SUB_USER_COLOR].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.USED;
                                        imiTextures[ImiTexture2D.Type.SUB_USER_COLOR].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                            else
                            {
                                if (imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus == ImiTexture2D.Status.USED)
                                {
                                    imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.READING;
                                    retDepth = ImiWrapper.GetColorPlayerTextureData(imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].textureData, controlPlayerId, null, -1, null, null, ref skeletonFrame, TIME_OUT, false);
                                    if (retDepth == ErrorCode.OK)
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.READY_TO_APPLY;
                                    }
                                    else
                                    {
                                        imiTextures[ImiTexture2D.Type.MAIN_USER_COLOR].texStatus = ImiTexture2D.Status.USED;
                                    }
                                }
                            }
                        }
                        else
                        {
                            //Log.e("You are not getting any player color texture!");
                            retDepth = ErrorCode.NONE;
                        }

                    }
                    if (retDepth == ErrorCode.OK)
                    {
                        //Debug.Log("Read Depth Data OK");
                        if (preferences.SkeletonStream || preferences.UserDepthAndSkeletonStream || preferences.DepthSkeletonStream)
                        {
                            try
                            {
                                ProcessSkeleton();
                            }
                            catch (Exception e)
                            {
                                Log.d("prcColorAndDepthData Process Skeleton Error :" + e.ToString());
                            }
                        }
                    }
                    else
                    {
                        //Debug.Log("Reading Depth Data Failed, ret =" + retDepth);
                    }

                }
                if (watch.ElapsedMilliseconds < THREAD_INTERVAL - 5)
                {
                    Thread.Sleep(THREAD_INTERVAL - (int)watch.ElapsedMilliseconds);
                }
            }
        }


        private ImiWrapper.ImiDeviceState deviceState = ImiWrapper.ImiDeviceState.IMI_DEVICE_STATE_DISCONNECT;
        private DateTime lastTime = DateTime.Now;
        private bool shouldRestart = false;
        public void CheckDeviceState()
        {
            if ((DateTime.Now - lastTime).TotalMilliseconds < 1000)
            {
                return;
            }
            //Debug.Log("Checking Device State");
            lastTime = DateTime.Now;
            ImiWrapper.ImiDeviceState newState = ImiWrapper.GetDeviceState();
            if (newState == deviceState)
            {
                return;
            }
            deviceState = newState;
            if (deviceState == ImiWrapper.ImiDeviceState.IMI_DEVICE_STATE_DISCONNECT)
            {
                Log.e("IMI_DEVICE_STATE_DISCONNECT");
                shouldRestart = IsDeviceWorking();
                if (deviceOpenState != DeviceOpenState.DEVICE_CLOSED)
                {
                    Close();
                }
                imiManager._OnDeviceDisconnected();
            }

            if (deviceState == ImiWrapper.ImiDeviceState.IMI_DEVICE_STATE_CONNECT)
            {
                Log.d("IMI_DEVICE_STATE_CONNECT");
                imiManager._OnDeviceConnected();
                if (shouldRestart)
                {
                    Open();
                }
                //Do not open automatically, the user should call opendevice when needed
            }
        }

        private void updateControlPlayerIdNear()
        {
            int oldControlId = controlPlayerId;
            controlPlayerId = -1;
            subControlPlayerId = -2;

            //分配主要玩家
            //如果没有被指定，选择最小的id作为主要玩家
            int controlI = 0;
            for (int i = 1; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                if (skeletonFrame.skeletonData[controlI].trackingState != ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {
                    controlI = i;
                    continue;
                }
                ImiWrapper.ImiSkeletonData skeletonData = skeletonFrame.skeletonData[i];
                //Debug.Log("Player " + i + " Tracking State = " + skeletonFrame.skeletonData[i].trackingState);
                if (skeletonData.trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {

                    if (skeletonData.position.z < skeletonFrame.skeletonData[controlI].position.z)
                    {
                        controlI = i;
                    }
                }
            }

            controlPlayerId = (int)skeletonFrame.skeletonData[controlI].trackingID;
            if (controlPlayerId == 0)
            {
                //Don't change it
                controlPlayerId = -1;
            }
            if (controlPlayerId != oldControlId && controlPlayerChangerObserver != null)
            {
                controlPlayerChangerObserver(oldControlId, controlPlayerId);
            }

            updateSubControlPlayerId();
        }
 
        private void updateControlPlayerIdLeft()
        {
            int left = -1, secondLeft = -1;

            //先找到最靠左的玩家 
            for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                if(skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_POSITION_ONLY ||
                    skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {
                    left = i;
                }
            }

            if (left == -1)
            {
                //处理没有追踪到玩家情况
                //不再强制追踪老的玩家
                if(controlPlayerId > 0)
                {
                    SetUnTrackingUserId(controlPlayerId);
                }
                if(subControlPlayerId > 0)
                {
                    SetUnTrackingUserId(subControlPlayerId);
                }
            } else {
                //有追踪到玩家情况
                for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
                {
                    if (skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_POSITION_ONLY ||
                        skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                    {
                        //如果有玩家比当前靠左玩家更左
                        if (skeletonFrame.skeletonData[i].position.x <= skeletonFrame.skeletonData[left].position.x)
                        {
                            secondLeft = left;
                            left = i;
                        }
                        else
                        {
                            //当前玩家在最左侧玩家的右边
                            if (secondLeft == -1 ||  /*第二左的玩家还没有出现，直接设置*/
                               skeletonFrame.skeletonData[i].position.x < skeletonFrame.skeletonData[secondLeft].position.x)
                            //当前玩家在第二左玩家的左侧
                            {
                                secondLeft = i;
                            }
                        }
                    }
                }

                //已经找到了最左玩家和次左玩家了,下面处理不再跟踪的玩家
                if(secondLeft == -1)
                {
                    //为了减少判断，如果次左玩家没有，就让次左玩家id设置为最左玩家id
                    secondLeft = left;
                }

                //老的控制玩家id与最左、次左均不相同, 不再跟踪老的控制玩家
                if (controlPlayerId > 0 &&
                    controlPlayerId != skeletonFrame.skeletonData[left].trackingID &&
                    controlPlayerId != skeletonFrame.skeletonData[secondLeft].trackingID)
                {
                    SetUnTrackingUserId(controlPlayerId);
                }

                //老的副控玩家id与最左、次左均不相同,不再跟踪老的副控玩家
                if (subControlPlayerId > 0 &&
                    subControlPlayerId != skeletonFrame.skeletonData[left].trackingID &&
                    subControlPlayerId != skeletonFrame.skeletonData[secondLeft].trackingID)
                {
                    SetUnTrackingUserId(subControlPlayerId);
                }
            }

            int oldControlId = controlPlayerId;
            int oldSubControlId = subControlPlayerId;

            if(left != -1)
            {
                //更新主控玩家为最左侧的玩家id
                controlPlayerId = (int)skeletonFrame.skeletonData[left].trackingID;
                if(controlPlayerId == 0)
                {
                    //如果玩家id有误，设置为负号，不应当出现这种情况
                    Log.e("Player State is Tracked or Position Only, but id is 0");
                    controlPlayerId = -1;
                }else
                {
                    //强制跟踪该玩家
                    if(oldControlId != controlPlayerId)
                        SetTrackingUserId(controlPlayerId);
                }

                if(secondLeft != -1 && secondLeft != left)
                {
                    //存在次左的玩家
                    subControlPlayerId = (int)skeletonFrame.skeletonData[secondLeft].trackingID;
                    if(subControlPlayerId == 0)
                    {
                        //如果玩家id有误，设置为负号，不应当出现这种情况
                        Log.e("Player State is Tracked or Position Only, but id is 0");
                        subControlPlayerId = -1;
                    }else
                    {
                       //强制跟踪该玩家,
                       if(oldSubControlId != subControlPlayerId)
                            SetTrackingUserId(subControlPlayerId);
                    }
                }
            }

            //回调主控玩家切换
            if (controlPlayerId != oldControlId && controlPlayerChangerObserver != null)
            {
                controlPlayerChangerObserver(oldControlId, controlPlayerId);
            }
        }

        private void updateSubControlPlayerId()
        {
            subControlPlayerId = -2;
            //分配次要玩家：除了主控玩家的另一个玩家，是次控玩家
            for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                ImiWrapper.ImiSkeletonData skeletonData = skeletonFrame.skeletonData[i];
                if (skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {
                    if (skeletonData.trackingID != controlPlayerId && skeletonData.trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                    {
                        subControlPlayerId = (int)skeletonData.trackingID;
                        break;
                    }
                }
            }
        }
 
        /// <summary>
        /// 更新主控玩家和次控玩家
        /// </summary>
        private void updateControlPlayerId()
        {
            //Fix Native Bug: some tracked user's position might be (0,0,0)
            for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                if (skeletonFrame.skeletonData[i].trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED
                    && skeletonFrame.skeletonData[i].position.z <= 0)
                {
                    skeletonFrame.skeletonData[i].position.x = 100;
                    skeletonFrame.skeletonData[i].position.y = 100;
                    skeletonFrame.skeletonData[i].position.z = 100;
                }
            }

            switch (controlMode)
            {
                case PlayerControlMode.NEAR:
                    updateControlPlayerIdNear();
                    break;
                case PlayerControlMode.LEFT:
                    updateControlPlayerIdLeft();
                    break;
                case PlayerControlMode.CUSTOM:
                    //如果玩家手动设置control player, 则不再自动更新
                    break;
                case PlayerControlMode.GESTURE:
                    updateSubControlPlayerId();
                    break;
                default:
                    break;
            }

        }

        //存储每一个玩家id 被track到的状态
        private bool[] playerTrackingState = new bool[ImiWrapper.MAX_TRACKED_PEOPLE_NUM + 2];

        //骨架数据处理.
        private bool ProcessSkeleton()
        {
            if (prcSkeletonLock)
            {
                return false;
            }

            prcSkeletonLock = true;
            if (skeletonFrame.skeletonData == null)
            {
                prcSkeletonLock = false;
                return true;
            }
            updateControlPlayerId();
            //process every skeleton in the frame

            //底层数据有问题，当一个玩家丢失后，底层不会将其状态置为NOT_TRACKED,而是直接
            //整个玩家数据都删除了（playerId丢失），为解决这个问题
            //首先设置所有player的tracked状态为false， 如果证实被track到，再设置为true
            //这样可以统计这一帧被track到的player
            //在循环结束后，比较这一帧被tracked到的player，与上一帧被tracked到的player有何变化
            //就可以确定哪些player是新进入的，哪些是丢失了的
            for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                playerTrackingState[i + 1] = false;
            }

            for (int i = 0; i < ImiWrapper.MAX_TRACKED_PEOPLE_NUM; i++)
            {
                ImiWrapper.ImiSkeletonData skeletonData = skeletonFrame.skeletonData[i];

                //Debug.Log("Player id = " + skeletonData.trackingID + "   " + skeletonData.trackingState + " ctrl id= " + controlPlayerId + " sub ctrl id =" + subControlPlayerId + "Positioni Z = " + skeletonData.position.z);

                //由于底层没有传给position only的情况，在此规避该错误
                //待底层bug修复后可删除
                //TOBE Deleted
                if (skeletonData.trackingState != ImiSkeletonTrackingState.IMI_SKELETON_TRACKED && 
                    skeletonData.position.z != 0)
                {
                    skeletonData.trackingState = ImiSkeletonTrackingState.IMI_SKELETON_POSITION_ONLY;
                    //Debug.Log("Player id = " + skeletonData.trackingID + " position only   " );
                }
                //End of TOBE Deleted

                //process the tracked skeleton
                if ((int)skeletonData.trackingID <= 0 || (int)skeletonData.trackingID > 7)
                {
                    //keep compatible with old devices
                    continue;
                }

                ImiPlayerInfo playerInfo = null;
                if (!playerInfos.TryGetValue((int)skeletonData.trackingID, out playerInfo))
                {
                    lock (playerInfos)
                    {
                        playerInfo = new ImiPlayerInfo();
                        playerInfo.SetUserId((int)skeletonData.trackingID);
                        playerInfos.Add((int)skeletonData.trackingID, playerInfo);
                        //Log.d("New Player Added, player index is " + playerInfo.userId);
                        //Debug.Log("uint player Id is =" + skeletonData.trackingID);
                    }
                }
                //Record Player tracking state and player position
                playerInfo.trackingState = skeletonData.trackingState;
                playerInfo.playerPosition.x = skeletonData.position.x;
                playerInfo.playerPosition.y = skeletonData.position.y;
                //这是底层的一个bug，被Track到的playerPosition可能为0， 如果为0，将其设置为最远
                playerInfo.playerPosition.z = skeletonData.position.z > 0 ? skeletonData.position.z : 100;

                //经测试，玩家丢失以后，playerId直接变为0，因此下面这种方法判断玩家丢失不可用
                //if (playerInfo.GetPlayerTracked() && skeletonData.trackingState != ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                //{
                //    Debug.Log("PlayerLost");
                //    //玩家丢失
                //    if (playerLostObserver != null)
                //    {
                //        mainThreadDispatcher.Enqueue(()=> {
                //            playerLostObserver(playerInfo.GetUserId());
                //        });
                //    }
                //}

                //判断哪些玩家是新进入的玩家
                if (!playerInfo.GetPlayerTracked() && skeletonData.trackingState == ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {
                    //玩家进入
                    if (playerEnteredObserver != null)
                    {
                        mainThreadDispatcher.Enqueue(() => {
                            playerEnteredObserver(playerInfo.GetUserId());
                        });
                    }
                }

                if (skeletonData.trackingState != ImiSkeletonTrackingState.IMI_SKELETON_TRACKED)
                {
                    continue;
                }

                playerInfo.SetPlayerTracked(true);
                playerTrackingState[playerInfo.GetUserId()] = true;
                int stateNotTracked = (int)ImiSkeletonPositionTrackingState.IMI_SKELETON_POSITION_NOT_TRACKED;

                playerInfo.SetPlayerPosition(skeletonData.position.x, skeletonData.position.y, skeletonData.position.z);

                for (int j = 0; j < TOTAL_JOINTS; j++)
                {
                    playerInfo.skeletons[j].isTracked = ((int)skeletonData.skeletonPositionTrackingStates[j] != stateNotTracked);
                    playerInfo.skeletons[j].position.x = skeletonData.skeletonPositions[j].x;
                    playerInfo.skeletons[j].position.y = skeletonData.skeletonPositions[j].y;
                    playerInfo.skeletons[j].position.z = skeletonData.skeletonPositions[j].z;
                    playerInfo.skeletons[j].positionV4 = skeletonData.skeletonPositions[j];
                }

                CalcLimbsAngular(playerInfo);

                if (preferences.isNeedJointsSpeed)
                {
                    CalcJointsSpeed(playerInfo);
                }

                if (preferences.isNeedJointsOri)
                {
                    CalcJointsOri(playerInfo);
                }

                FixNeedPos(playerInfo);

                //Update Gestures
                if (imiGestureManager.ProInited)
                {
                    for (int j = 0; j < TOTAL_JOINTS; j++)
                    {
                        skeletonData.skeletonPositions[j].x *= 1000;
                        skeletonData.skeletonPositions[j].y *= 1000;
                        skeletonData.skeletonPositions[j].z *= 1000;
                    }
                    int error = ImiGestureWrapper.ImiUpdateGesture(skeletonData.trackingID, ref skeletonData);
                    for (int j = 0; j < TOTAL_JOINTS; j++)
                    {
                        skeletonData.skeletonPositions[j].x /= 1000;
                        skeletonData.skeletonPositions[j].y /= 1000;
                        skeletonData.skeletonPositions[j].z /= 1000;
                    }
                }

            }

            //将这一帧的玩家状态与上一帧比较,查找出丢失了的玩家
            //因为底层在丢失玩家后，玩家id会一起置为0
            List<int> keys = new List<int>(playerInfos.Keys);
            for (int i = 0; i < keys.Count; i++)
            {
                int tempUid = playerInfos[keys[i]].GetUserId();
                if (tempUid > 0 && tempUid < 7)
                {
                    if (playerInfos[keys[i]].GetPlayerTracked() && !playerTrackingState[tempUid])
                    {
                        playerInfos[keys[i]].SetPlayerTracked(false);
                        if (playerLostObserver != null)
                        {
                            mainThreadDispatcher.Enqueue(() =>
                            {
                                playerLostObserver(tempUid);
                            });
                        }
                    }
                }
            }

            imiGestureManager.Update();

            prcSkeletonLock = false;
            return true;
        }

        bool[] confidenceArr = new bool[100];
        int confidenceP = 0;

        private bool standStraight(ImiPlayerInfo controlPlayerInfo)
        {
            bool standStraight = controlPlayerInfo.GetSkeletons()[ImiGesture.leftHipIndex].isTracked &&
                    controlPlayerInfo.GetSkeletons()[ImiGesture.leftKneeIndex].isTracked &&
                    controlPlayerInfo.GetSkeletons()[ImiGesture.rightHipIndex].isTracked &&
                    controlPlayerInfo.GetSkeletons()[ImiGesture.rightKneeIndex].isTracked &&
                    Math.Abs(controlPlayerInfo.GetSkeletons()[ImiGesture.leftHipIndex].position.z -
                    controlPlayerInfo.GetSkeletons()[ImiGesture.rightHipIndex].position.z) < 0.04f &&
                    (controlPlayerInfo.GetPlayerLimbsAngular()[ImiGesture.leftThighIndex].y < 0 &&
                    controlPlayerInfo.GetPlayerLimbsAngular()[ImiGesture.rightThighIndex].y < 0) ||
                    (Math.Abs(controlPlayerInfo.GetPlayerLimbsAngular()[ImiGesture.leftThighIndex].y) > 83.0f &&
                    Math.Abs(controlPlayerInfo.GetPlayerLimbsAngular()[ImiGesture.rightThighIndex].y) > 83.0f);
            confidenceArr[confidenceP++] = standStraight;
            if(confidenceP == 100)
            {
                confidenceP = 0;
            }
            int confidence = 0;
            for(int i = 0; i< 100; i++)
            {
                if (confidenceArr[i])
                {
                    confidence++;
                }
            }
            return confidence > 90;
        }

        private bool standStill(ImiPlayerInfo controlPlayerInfo)
        {
            return controlPlayerInfo.GetSkeletons()[ImiGesture.hipCenterIndex].isTracked &&
                    Math.Abs(controlPlayerInfo.GetPlayerJointsSpeed()[ImiGesture.hipCenterIndex].z) < 0.05f &&
                    Math.Abs(controlPlayerInfo.GetPlayerJointsSpeed()[ImiGesture.hipCenterIndex].y) < 0.05f;
        }

 
        //计算玩家站直身体hipcenter高度.
        private void FixNeedPos(ImiPlayerInfo controlPlayerInfo)
        {
            if (!controlPlayerInfo.isOrigFixRec)
            {
                if (controlPlayerInfo.GetPlayerPosition().z > 1.2f &&
                    standStraight(controlPlayerInfo) &&
                    standStill(controlPlayerInfo))
                {
                    controlPlayerInfo.fixFrameCount++;
                    controlPlayerInfo.isOrigFixRec = true;
                    controlPlayerInfo.fixPosZ = (controlPlayerInfo.fixPosZ * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetPlayerPosition().z) / controlPlayerInfo.fixFrameCount;
                    controlPlayerInfo.fixHipPosY = (controlPlayerInfo.fixHipPosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.hipCenterIndex].position.y) / controlPlayerInfo.fixFrameCount;
                    controlPlayerInfo.fixLeftKneePosY = (controlPlayerInfo.fixLeftKneePosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.leftKneeIndex].position.y) / controlPlayerInfo.fixFrameCount;
                    controlPlayerInfo.fixRightKneePosY = (controlPlayerInfo.fixRightKneePosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.rightKneeIndex].position.y) / controlPlayerInfo.fixFrameCount;
                }
            }
            else
            {
                if (controlPlayerInfo.fixFrameCount > 1000 || Math.Abs(controlPlayerInfo.GetPlayerPosition().z - controlPlayerInfo.fixPosZ) > 0.2f)
                {
                    controlPlayerInfo.fixFrameCount = 1;
                }else
                {
                    if (controlPlayerInfo.GetPlayerPosition().z > 1.2f &&
                        standStraight(controlPlayerInfo) &&
                        standStill(controlPlayerInfo))
                    {
                        controlPlayerInfo.fixFrameCount++;
                        controlPlayerInfo.fixPosZ = (controlPlayerInfo.fixPosZ * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetPlayerPosition().z) / controlPlayerInfo.fixFrameCount;
                        controlPlayerInfo.fixHipPosY = (controlPlayerInfo.fixHipPosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.hipCenterIndex].position.y) / controlPlayerInfo.fixFrameCount;
                        controlPlayerInfo.fixLeftKneePosY = (controlPlayerInfo.fixLeftKneePosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.leftKneeIndex].position.y) / controlPlayerInfo.fixFrameCount;
                        controlPlayerInfo.fixRightKneePosY = (controlPlayerInfo.fixRightKneePosY * (controlPlayerInfo.fixFrameCount - 1) + controlPlayerInfo.GetSkeletons()[ImiGesture.rightKneeIndex].position.y) / controlPlayerInfo.fixFrameCount;
                    }

                }
            }

        }


        //计算肢体角度.
        private void CalcLimbsAngular(ImiPlayerInfo playerInfo)
        {
            for (int j = 0; j < (int)Limb.Count; j++)
            {
                Vector3 limbAng = new Vector3(-1.0f, -1.0f, -1.0f);
                switch ((Limb)Enum.ToObject(typeof(Limb), j))
                {
                    case Limb.Left_Forearm:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftHandIndex, ImiGesture.leftElbowIndex);
                        break;
                    case Limb.Left_Forearm_Wrist:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftWristIndex, ImiGesture.leftElbowIndex);
                        break;
                    case Limb.Left_Postbrachium:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftElbowIndex, ImiGesture.leftShoulderIndex);
                        break;
                    case Limb.Left_Calf:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftFootIndex, ImiGesture.leftKneeIndex);
                        break;
                    case Limb.Left_Calf_Ankle:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftAnkleIndex, ImiGesture.leftKneeIndex);
                        break;
                    case Limb.Left_Thigh:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.leftKneeIndex, ImiGesture.leftHipIndex);
                        break;
                    case Limb.Right_Forearm:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightHandIndex, ImiGesture.rightElbowIndex);
                        break;
                    case Limb.Right_Forearm_Wrist:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightWristIndex, ImiGesture.rightElbowIndex);
                        break;
                    case Limb.Right_Postbrachium:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightElbowIndex, ImiGesture.rightShoulderIndex);
                        break;
                    case Limb.Right_Calf:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightFootIndex, ImiGesture.rightKneeIndex);
                        break;
                    case Limb.Right_Calf_Ankle:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightAnkleIndex, ImiGesture.rightKneeIndex);
                        break;
                    case Limb.Right_Thigh:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.rightKneeIndex, ImiGesture.rightHipIndex);
                        break;
                    case Limb.Spine:
                        limbAng = CalcLimbAngular(playerInfo, ImiGesture.spineIndex, ImiGesture.shoulderCenterIndex);
                        break;
                }
                playerInfo.SetPlayerLimbsAngular(j, limbAng);
            }
        }

        private Vector3 CalcLimbAngular(ImiPlayerInfo playerInfo, int joint1, int joint2)
        {
            //Vector3[] playerJointsPos = playerInfo.GetPlayerJointsPos();
            //bool[] playerJointsTracked = playerInfo.GetPlayerJointsTracked();
            ImiSkeleton[] skeletons = playerInfo.GetSkeletons();

            if (skeletons[joint1].isTracked && skeletons[joint2].isTracked)
            {
                float kYX = (skeletons[joint1].position.y - skeletons[joint2].position.y) /
                            (skeletons[joint1].position.x - skeletons[joint2].position.x);
                float angYX = (float)(Math.Atan(kYX) * (180 / Math.PI));

                float kXZ = (skeletons[joint1].position.x - skeletons[joint2].position.x) /
                            (skeletons[joint1].position.z - skeletons[joint2].position.z);
                float angXZ = (float)(Math.Atan(kXZ) * (180 / Math.PI));

                float kYZ = (skeletons[joint1].position.y - skeletons[joint2].position.y) /
                            (skeletons[joint1].position.z - skeletons[joint2].position.z);
                float angYZ = (float)(Math.Atan(kYZ) * (180 / Math.PI));

                return new Vector3(angYX, angYZ, angXZ);
            }
            return new Vector3(-1.0f, -1.0f, -1.0f);
        }


        //计算关节点四元数.
        private void CalcJointsOri(ImiPlayerInfo playerInfo)
        {
            Vector3 vx;
            Vector3 vy;
            Vector3 vz;
            ImiSkeleton[] skeletons = playerInfo.GetSkeletons();

            // NUI_SKELETON_POSITION_HIP_CENTER
            if (skeletons[ImiGesture.hipCenterIndex].isTracked && skeletons[ImiGesture.spineIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.hipCenterIndex, ImiGesture.spineIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftHipIndex, ImiGesture.rightHipIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);

                // make a correction of about 40 degrees back to the front
                //quat *= Quaternion.Euler(-40, 0, 0);
                skeletons[ImiGesture.hipCenterIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_SPINE
            if (skeletons[ImiGesture.spineIndex].isTracked && skeletons[ImiGesture.shoulderCenterIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.spineIndex, ImiGesture.shoulderCenterIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftShoulderIndex, ImiGesture.rightShoulderIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);
                skeletons[ImiGesture.spineIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_SHOULDER_CENTER
            if (skeletons[ImiGesture.shoulderCenterIndex].isTracked && skeletons[ImiGesture.headIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.shoulderCenterIndex, ImiGesture.headIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftShoulderIndex, ImiGesture.rightShoulderIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);
                skeletons[ImiGesture.shoulderCenterIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_HEAD
            if (skeletons[ImiGesture.shoulderCenterIndex].isTracked && skeletons[ImiGesture.headIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.shoulderCenterIndex, ImiGesture.headIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftShoulderIndex, ImiGesture.rightShoulderIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);
                skeletons[ImiGesture.headIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_SHOULDER_LEFT
            if (skeletons[ImiGesture.leftShoulderIndex].isTracked && skeletons[ImiGesture.leftElbowIndex].isTracked)
            {
                vx = -GetPositionBetweenIndices(skeletons, ImiGesture.leftShoulderIndex, ImiGesture.leftElbowIndex);
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.leftElbowIndex, ImiGesture.leftWristIndex);
                Quaternion quat = Utils.MakeQuaternionFromXY(vx, vy);
                skeletons[ImiGesture.leftShoulderIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_ELBOW_LEFT
            if (skeletons[ImiGesture.leftElbowIndex].isTracked && skeletons[ImiGesture.leftWristIndex].isTracked)
            {
                vx = -GetPositionBetweenIndices(skeletons, ImiGesture.leftElbowIndex, ImiGesture.leftWristIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.leftShoulderIndex, ImiGesture.leftElbowIndex);
                Quaternion quat = Utils.MakeQuaternionFromXY(vx, vy);
                skeletons[ImiGesture.leftElbowIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_WRIST_LEFT
            if (skeletons[ImiGesture.leftWristIndex].isTracked && skeletons[ImiGesture.leftHandIndex].isTracked)
            {
                vx = -GetPositionBetweenIndices(skeletons, ImiGesture.leftWristIndex, ImiGesture.leftHandIndex);
                Quaternion quat = Utils.MakeQuaternionFromX(vx, false);
                skeletons[ImiGesture.leftWristIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_HAND_LEFT:
            if (skeletons[ImiGesture.leftWristIndex].isTracked && skeletons[ImiGesture.leftHandIndex].isTracked)
            {
                vx = -GetPositionBetweenIndices(skeletons, ImiGesture.leftWristIndex, ImiGesture.leftHandIndex);
                Quaternion quat = Utils.MakeQuaternionFromX(vx, false);
                skeletons[ImiGesture.leftHandIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_SHOULDER_RIGHT
            if (skeletons[ImiGesture.rightShoulderIndex].isTracked && skeletons[ImiGesture.rightElbowIndex].isTracked)
            {
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.rightShoulderIndex, ImiGesture.rightElbowIndex);
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.rightElbowIndex, ImiGesture.rightWristIndex);
                Quaternion quat = Utils.MakeQuaternionFromXY(vx, vy);
                skeletons[ImiGesture.rightShoulderIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_ELBOW_RIGHT
            if (skeletons[ImiGesture.rightElbowIndex].isTracked && skeletons[ImiGesture.rightWristIndex].isTracked)
            {
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.rightElbowIndex, ImiGesture.rightWristIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.rightShoulderIndex, ImiGesture.rightElbowIndex);
                Quaternion quat = Utils.MakeQuaternionFromXY(vx, vy);
                skeletons[ImiGesture.rightElbowIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_WRIST_RIGHT
            if (skeletons[ImiGesture.rightWristIndex].isTracked && skeletons[ImiGesture.rightHandIndex].isTracked)
            {
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.rightWristIndex, ImiGesture.rightHandIndex);
                Quaternion quat = Utils.MakeQuaternionFromX(vx, true);
                skeletons[ImiGesture.rightWristIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_HAND_RIGHT
            if (skeletons[ImiGesture.rightWristIndex].isTracked && skeletons[ImiGesture.rightHandIndex].isTracked)
            {
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.rightWristIndex, ImiGesture.rightHandIndex);
                Quaternion quat = Utils.MakeQuaternionFromX(vx, true);
                skeletons[ImiGesture.rightHandIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_HIP_LEFT
            if (skeletons[ImiGesture.leftHipIndex].isTracked && skeletons[ImiGesture.leftKneeIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.leftKneeIndex, ImiGesture.leftHipIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftHipIndex, ImiGesture.rightHipIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);
                skeletons[ImiGesture.leftHipIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_KNEE_LEFT
            if (skeletons[ImiGesture.leftKneeIndex].isTracked && skeletons[ImiGesture.leftAnkleIndex].isTracked)
            {
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.leftKneeIndex, ImiGesture.leftAnkleIndex);
                vz = -GetPositionBetweenIndices(skeletons, ImiGesture.leftAnkleIndex, ImiGesture.leftFootIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.leftKneeIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_ANKLE_LEFT
            if (skeletons[ImiGesture.leftAnkleIndex].isTracked && skeletons[ImiGesture.leftFootIndex].isTracked)
            {
                vz = GetPositionBetweenIndices(skeletons, ImiGesture.leftFootIndex, ImiGesture.leftAnkleIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.leftKneeIndex, ImiGesture.leftAnkleIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.leftAnkleIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_FOOT_LEFT
            if (skeletons[ImiGesture.leftAnkleIndex].isTracked && skeletons[ImiGesture.leftFootIndex].isTracked)
            {
                vz = GetPositionBetweenIndices(skeletons, ImiGesture.leftFootIndex, ImiGesture.leftAnkleIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.leftKneeIndex, ImiGesture.leftAnkleIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.leftFootIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_HIP_RIGHT
            if (skeletons[ImiGesture.rightHipIndex].isTracked && skeletons[ImiGesture.rightKneeIndex].isTracked)
            {
                vy = GetPositionBetweenIndices(skeletons, ImiGesture.rightKneeIndex, ImiGesture.rightHipIndex);
                vx = GetPositionBetweenIndices(skeletons, ImiGesture.leftHipIndex, ImiGesture.rightHipIndex);
                Quaternion quat = Utils.MakeQuaternionFromYX(vx, vy);
                skeletons[ImiGesture.rightHipIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_KNEE_RIGHT
            if (skeletons[ImiGesture.rightKneeIndex].isTracked && skeletons[ImiGesture.rightAnkleIndex].isTracked)
            {
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.rightKneeIndex, ImiGesture.rightAnkleIndex);
                vz = -GetPositionBetweenIndices(skeletons, ImiGesture.rightAnkleIndex, ImiGesture.rightFootIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.rightKneeIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_ANKLE_RIGHT
            if (skeletons[ImiGesture.rightAnkleIndex].isTracked && skeletons[ImiGesture.rightFootIndex].isTracked)
            {
                vz = GetPositionBetweenIndices(skeletons, ImiGesture.rightFootIndex, ImiGesture.rightAnkleIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.rightKneeIndex, ImiGesture.rightAnkleIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.rightAnkleIndex].orientation = quat;
            }

            // NUI_SKELETON_POSITION_FOOT_RIGHT
            if (skeletons[ImiGesture.rightAnkleIndex].isTracked && skeletons[ImiGesture.rightFootIndex].isTracked)
            {
                vz = GetPositionBetweenIndices(skeletons, ImiGesture.rightFootIndex, ImiGesture.rightAnkleIndex);
                vy = -GetPositionBetweenIndices(skeletons, ImiGesture.rightKneeIndex, ImiGesture.rightAnkleIndex);
                Quaternion quat = Utils.MakeQuaternionFromYZ(vy, vz);
                skeletons[ImiGesture.rightFootIndex].orientation = quat;
            }
        }

        private Vector3 GetPositionBetweenIndices(ImiSkeleton[] skeletons, int jointIndexOne, int jointIndexTwo)
        {
            Vector3 pVecOne = skeletons[(int)jointIndexOne].position;
            Vector3 pVecTwo = skeletons[(int)jointIndexTwo].position;

            return pVecTwo - pVecOne;
        }

        public ImiPlayerInfo GetPlayerInfo(int userIndex)
        {
            if (playerInfos.ContainsKey(userIndex))
            {
                return playerInfos[userIndex];
            }
            else
            {
                return null;
            }
        }

        public ImiPlayerInfo GetMainPlayerInfo()
        {
            return GetPlayerInfo(controlPlayerId);
        }

        public ImiPlayerInfo GetSubPlayerInfo()
        {
            return GetPlayerInfo(subControlPlayerId);
        }


        //计算关节点速度.
        private void CalcJointsSpeed(ImiPlayerInfo playerInfo)
        {
            ImiSkeleton[] skeletons = playerInfo.GetSkeletons();
            int userIndex = playerInfo.GetUserId();

            for (int j = 0; j < TOTAL_JOINTS; j++)
            {
                if (skeletons[j].isTracked)
                {
                    if (distanceDatas[userIndex, j].Count < 2)
                    {
                        distanceDatas[userIndex, j].Add(skeletons[j].position);
                        timeDatas[userIndex, j].Add(DateTime.Now);
                    }
                    else
                    {
                        TimeSpan ts = timeDatas[userIndex, j][1] - timeDatas[userIndex, j][0];

                        Vector4 tempData;
                        if (preferences.isNeedVecSpeed)
                        {
                            tempData = VecVector4((distanceDatas[userIndex, j][1] - distanceDatas[userIndex, j][0]), (float)(ts.TotalMilliseconds / 100));
                        }
                        else
                        {
                            tempData = AbsVector4((distanceDatas[userIndex, j][1] - distanceDatas[userIndex, j][0]), (float)(ts.TotalMilliseconds / 100));
                        }


                        distanceDatas[userIndex, j].RemoveAt(0);
                        distanceDatas[userIndex, j].Add(skeletons[j].position);
                        timeDatas[userIndex, j].RemoveAt(0);
                        timeDatas[userIndex, j].Add(DateTime.Now);

                        if (speedDatas[userIndex, j].Count < speedFrameLimit)
                        {
                            speedDatas[userIndex, j].Add(tempData);
                        }
                        else
                        {
                            Vector4 allDistanceTime = CalcDisTime(speedDatas[userIndex, j]);

                            playerInfos[userIndex].GetSkeletons()[j].speed = new Vector3(allDistanceTime.x / allDistanceTime.w, allDistanceTime.y / allDistanceTime.w, allDistanceTime.z / allDistanceTime.w);

                            speedDatas[userIndex, j].RemoveAt(0);
                            speedDatas[userIndex, j].Add(tempData);
                        }
                    }
                }
            }
        }

        private Vector4 AbsVector4(Vector3 v3, float seconds)
        {
            return new Vector4(Math.Abs(v3.x), Math.Abs(v3.y), Math.Abs(v3.z), seconds);
        }

        private Vector4 VecVector4(Vector3 v3, float seconds)
        {
            return new Vector4(v3.x, v3.y, v3.z, seconds);
        }

        private Vector4 CalcDisTime(List<Vector4> datas)
        {
            float distanceX = 0;
            float distanceY = 0;
            float distanceZ = 0;
            float time = 0;
            for (int i = 0; i < datas.Count; i++)
            {
                distanceX += datas[i].x;
                distanceY += datas[i].y;
                distanceZ += datas[i].z;
                time += datas[i].w;
            }
            return new Vector4(distanceX, distanceY, distanceZ, time);
        }


        public Texture2D GetImiTexture(ImiTexture2D.Type type)
        {
            if(imiTextures == null)
            {
                return null;
            }
            if (!imiTextures.ContainsKey(type))
            {
                Log.d("Getting Tex, type  = " + type);
                imiTextures.Add(type, ImiTexture2DFactory.CreateTexture(type));
            }
            return imiTextures[type].Texture;
        }


        public Texture2D GetColorTexture()
        {

#if UNITY_ANDROID && !UNITY_EDITOR
            if (imiManager.IsPerformance)
            {
                return allUserColorTexImproved;
            }
            else
            {
                return GetImiTexture(ImiTexture2D.Type.COLOR);
            }
#else
            return GetImiTexture(ImiTexture2D.Type.COLOR);
#endif
        }

        public Texture2D GetDepthTexture()
        {
            return GetImiTexture(ImiTexture2D.Type.DEPTH);
        }

        public Texture2D GetUserDepthTexture()
        {
            return GetImiTexture(ImiTexture2D.Type.USER_DEPTH);
        }

        public Texture2D GetMainUserDepthTexture()
        {
            return GetImiTexture(ImiTexture2D.Type.MAIN_USER_DEPTH);
        }

        public Texture2D GetSubUserDepthTexture()
        {
            return GetImiTexture(ImiTexture2D.Type.SUB_USER_DEPTH);
        }

        private bool checkResolution()
        {
            //if (preferences.colorResolution.type != preferences.depthResolution.type)
            //{
            //    Log.e("Cannot Cutout Color Texuture Because Depth Resolution is not equal to Color Resolution");
            //    return false;
            //}
            return true;
        }

        public Texture2D GetMainUserColorTexture()
        {
            return checkResolution() ? GetImiTexture(ImiTexture2D.Type.MAIN_USER_COLOR) : null;
        }

        public Texture2D GetSubUserColorTexture()
        {
            return checkResolution() ? GetImiTexture(ImiTexture2D.Type.SUB_USER_COLOR) : null;
        }

        public Texture2D GetMainUserColorTextureInCenter()
        {
            return checkResolution() ? GetImiTexture(ImiTexture2D.Type.MAIN_USER_COLOR_CENTERED) : null;
        }

        public Texture2D GetSubUserColorTextureInCenter()
        {
            return checkResolution() ? GetImiTexture(ImiTexture2D.Type.SUB_USER_COLOR_CENTERED) : null;
        }

        public bool IsDeviceWorking()
        {
            return deviceOpenState == DeviceOpenState.WORKING;
        }

        internal void setControlPlayerIdWithoutModeChanging(int userId)
        {
            if (this.controlPlayerId != userId && controlPlayerChangerObserver != null)
            {
                controlPlayerChangerObserver(this.controlPlayerId, userId);
            }
            this.controlPlayerId = userId;
        }

        /// <summary>
        /// 设置控制玩家ID.
        /// </summary>
        /// <param name="index"></param>
        public void SetControlPlayerId(int userId)
        {
            controlMode = PlayerControlMode.CUSTOM;
            if (this.controlPlayerId != userId && controlPlayerChangerObserver != null)
            {
                SetUnTrackingUserId(controlPlayerId);
                SetTrackingUserId(userId);
                controlPlayerChangerObserver(this.controlPlayerId, userId);
            }
            this.controlPlayerId = userId;
        }

        public void SetSubControlPlayerId(int userId)
        {
            controlMode = PlayerControlMode.CUSTOM;
            if (this.controlPlayerId != userId && controlPlayerChangerObserver != null)
            {
                SetUnTrackingUserId(subControlPlayerId);
                SetTrackingUserId(userId);
            }
            this.subControlPlayerId = userId;
        }

        public int GetControlPlayerId()
        {
            return this.controlPlayerId;
        }

        public int GetSubPlayerId()
        {
            return this.subControlPlayerId;
        }

        public ImiResolution GetColorResolution()
        {
            return preferences.colorResolution;
        }

        public ImiResolution GetDepthResolution()
        {
            return preferences.depthResolution;
        }

        public void SetControlPlayerChangeListener(ImiManager.OnControlPlayerChanged observer)
        {
            this.controlPlayerChangerObserver += observer;
        }

        public void SetPlayerEnteredListener(ImiManager.OnPlayerEntered observer)
        {
            this.playerEnteredObserver += observer;
        }

        public void SetPlayerLostListener(ImiManager.OnPlayerLost observer)
        {
            this.playerLostObserver += observer;
        }

        public void SetDepthDataProcessor(ImiManager.DepthDataProcessor processor)
        {
            if (depthDataRaw == null)
            {
                depthDataRaw = new ushort[preferences.depthResolution.Size];
            }
            this.depthDataProcessor = processor;
        }

        public void SetColorDataProcessor(ImiManager.ColorDataProcessor processor)
        {
            if (colorDataRaw == null)
            {
                colorDataRaw = new byte[preferences.colorResolution.Size * 4];
            }
            this.colorDataProcessor = processor;
        }

        public void SetRawDataProcessor(ImiManager.RawDataProcessor processor)
        {
            if (colorDataRaw == null)
            {
                colorDataRaw = new byte[preferences.colorResolution.Size * 4];
            }
            if (depthDataRaw == null)
            {
                depthDataRaw = new ushort[preferences.depthResolution.Size];
            }
            this.rawDataProcessor = processor;
        }

        public Vector3[] GetNormalizedSkeleton(int userIndex)
        {
            if (playerInfos != null && playerInfos.ContainsKey(userIndex))
            {
                if (userIndex == controlPlayerId || userIndex == subControlPlayerId)
                {
                    return playerInfos[userIndex].GetNormalizedSkeletonPos();
                }
            }
            return new Vector3[(int)ImiSkeleton.Index.COUNT];
        }

        public Texture2D GetColorTextureInCenter(int userIndex)
        {
            if (userIndex == controlPlayerId)
            {
                return GetMainUserColorTextureInCenter();
            }
            else if (userIndex == subControlPlayerId)
            {
                return GetSubUserColorTextureInCenter();
            }
            else
            {
                return null;
            }
        }

        public ImiDeviceInfo GetDeviceInfo()
        {
            ImiDeviceInfo deviceInfo = new ImiDeviceInfo();
            ErrorCode e = ImiWrapper.GetDeviceInfo(ref deviceInfo);
            return deviceInfo;
        }

        public void SetPlayerControlMode(PlayerControlMode mode)
        {
            this.controlMode = mode;
            if (mode == PlayerControlMode.GESTURE)
            {
                if (!ImiGestureManager.GetInstance().ContainsGesture(typeof(Calibration)))
                {
                    ImiGestureManager.GetInstance().AddGesture(typeof(Calibration), null);
                }
            }
        }

        public PlayerControlMode GetPlayerControlMode()
        {
            return this.controlMode;
        }

        public bool SetTrackingUserId(int userId)
        {
            if(ImiWrapper.SetTrackingUserId(userId,true) == ErrorCode.OK)
            {
                return true;
            }else
            {
                return false;
            }
        }

        public bool SetUnTrackingUserId(int userId)
        {
            if(ImiWrapper.SetTrackingUserId(userId,false) == ErrorCode.OK)
            {
                return true;
            }else
            {
                return false;
            }
        }
 

    }

}
